﻿#region 文件信息

/*----------------------------------------------------------------
//
// 文件名称：
// 文件功能描述：
// 设计要求：
//
// 文 件 名：    RpcServerExtension
// 创建者：      杨程
// 创建日期：	    2023/1/6 17:25:25

//----------------------------------------------------------------*/

#endregion

namespace Vampirewal.Admin.Server.RpcServices;

/// <summary>
/// RPC扩展
/// </summary>
public static class RpcServerExtension
{
    /// <summary>
    /// 验证是否Http连接并返回User信息
    /// </summary>
    /// <param name="callContext"></param>
    /// <param name="user"></param>
    /// <returns></returns>
    public static void CheckCaller(this ICallContext callContext, out Sys_User user)
    {
        if (callContext.Caller is TcpTouchRpcSocketClient caller)
        {
            user = JsonConvert.DeserializeObject<Sys_User>(caller.ID);
        }
        else
        {
            throw new Exception("不是正确的用户调用接口！");
        }
    }

    /// <summary>
    /// 验证是否Http连接
    /// </summary>
    /// <param name="callContext"></param>
    /// <returns></returns>
    public static bool CheckTcpClient(this ICallContext callContext)
    {
        if (callContext.Caller is TcpTouchRpcSocketClient caller)
            return true;
        return false;
    }

    /// <summary>
    /// 从连接的ID中获取用户信息
    /// </summary>
    /// <param name="callContext"></param>
    /// <returns></returns>
    public static Sys_User? GetClientUser(this ICallContext callContext)
    {
        try
        {
            return JsonConvert.DeserializeObject<Sys_User>(((TcpTouchRpcSocketClient)callContext).ID);
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    /// 生成API文档
    /// <para>★必须放在注册RPC服务之后★</para>
    /// </summary>
    /// <param name="store"></param>
    public static void GenerateApiDocument(this RpcStore store)
    {
        //var aaa= Assembly.LoadFrom("Vampirewal.Admin.Client.Common");

        var Methods = store.GetAllMethods();

        StringBuilder sb = new StringBuilder();
        string curServiceName = string.Empty;

        foreach (MethodInstance method in Methods)
        {
            string ServiceName = method.ServerType.Name;

            string text = "M:" + ToTypeNameComment(method.Info.DeclaringType) + "." + method.Name;
            string Servicetext = "T:" + ToTypeNameComment(method.Info.DeclaringType);
            string text2 = AppDomain.CurrentDomain.BaseDirectory + method.Info.DeclaringType.Assembly.GetName().Name + ".xml";
            XmlDocument xmlDocument = new XmlDocument();
            if (!File.Exists(text2))
            {
                return;
            }

            xmlDocument.Load(text2);

            foreach (XmlNode childNode in xmlDocument["doc"]["members"].ChildNodes)
            {
                if (childNode.Attributes != null && childNode.Attributes["name"].Value == Servicetext)
                {
                    if (ServiceName != curServiceName)
                    {
                        XmlElement xmlElement = childNode["summary"];

                        if (!string.IsNullOrEmpty(curServiceName))
                        {
                            sb.AppendLine("<tr>");
                            sb.AppendLine($"    <td><span style=\"color: gray\" >----------</span></td>");
                            sb.AppendLine($"    <td><span style=\"color: gray\">----------</span></td>");
                            sb.AppendLine($"    <td><span style=\"color: gray\">----------</span></td>");
                            sb.AppendLine("</tr>");
                        }

                        sb.AppendLine("<tr>");
                        sb.AppendLine($"    <td><span style=\"color: red\">{ServiceName}</span></td>");
                        sb.AppendLine($"    <td><span style=\"color: red\">↓↓↓{xmlElement.InnerText.Trim()}↓↓↓</span></td>");
                        sb.AppendLine($"    <td><span style=\"color: red\"> </span></td>");
                        sb.AppendLine("</tr>");

                        curServiceName = ServiceName;
                    }
                }

                if (childNode.Attributes != null && (childNode.Attributes["name"].Value == text || childNode.Attributes["name"].Value.StartsWith(text + "(")))
                {
                    //return childNode.Clone();
                    XmlElement xmlElement = childNode["summary"];
                    if (xmlElement != null)
                    {
                        //string abc= xmlElement.InnerText.Trim();

                        sb.AppendLine("<tr>");
                        sb.AppendLine($"    <td><span>{method.Name}</span></td>");
                        sb.AppendLine($"    <td><span style=\"color: gray\">{xmlElement.InnerText.Trim()}</span></td>");
                        sb.AppendLine($"    <td><span style=\"color: gray\">{method.ServerType.FullName.ToLower()}.{method.Name.ToLower()}</span></td>");
                        sb.AppendLine("</tr>");
                    }
                }
            }
        }

        string html = GetTemplateByFileName("Index");

        html = html.Replace("{tr}", sb.ToString());

        string savePath = AppDomain.CurrentDomain.BaseDirectory + "html\\index.html";

        //string save2 = "D:\\Vampirewal.Admin\\Vampirewal.Admin.Client.Common\\test.cs";
        if (!Directory.Exists($"{AppDomain.CurrentDomain.BaseDirectory}html"))
        {
            Directory.CreateDirectory($"{AppDomain.CurrentDomain.BaseDirectory}html");
        }
        File.WriteAllText(savePath, html);
        //File.WriteAllText(save2, html);
    }

    /// <summary>
    /// 直接在Vampirewal.Admin.ShareCommon中生成Rpc方法
    /// </summary>
    /// <param name="store"></param>
    public static void GenerateRpcMethodFile(this RpcStore store)
    {
        var Methods = store.GetAllMethods();

        string curServiceName = string.Empty;

        List<GenerateRPC> rpcList = new List<GenerateRPC>();

        foreach (MethodInstance method in Methods)
        {
            string ServiceName = method.ServerType.Name;

            string text = "M:" + ToTypeNameComment(method.Info.DeclaringType) + "." + method.Name;
            string Servicetext = "T:" + ToTypeNameComment(method.Info.DeclaringType);
            string text2 = AppDomain.CurrentDomain.BaseDirectory + method.Info.DeclaringType.Assembly.GetName().Name + ".xml";
            XmlDocument xmlDocument = new XmlDocument();
            if (!File.Exists(text2))
            {
                return;
            }

            xmlDocument.Load(text2);

            var Grpc = rpcList.FirstOrDefault(f => f.ServerName.Equals(ServiceName));

            if (Grpc == null)
            {
                Grpc = new GenerateRPC()
                {
                    ServerName = ServiceName,
                    Paramters = new List<RpcMethod>()
                };
            }

            foreach (XmlNode childNode in xmlDocument["doc"]["members"].ChildNodes)
            {
                if (childNode.Attributes != null && childNode.Attributes["name"].Value == Servicetext)
                {
                    if (ServiceName != curServiceName)
                    {
                        XmlElement xmlElement = childNode["summary"];

                        Grpc.ServerDesc = xmlElement.InnerText.Trim();
                        curServiceName = ServiceName;
                    }
                }

                if (childNode.Attributes != null && (childNode.Attributes["name"].Value == text || childNode.Attributes["name"].Value.StartsWith(text + "(")))
                {
                    XmlElement xmlElement = childNode["summary"];
                    if (xmlElement != null)
                    {
                        RpcMethod para = new RpcMethod()
                        {
                            MethodName = method.Name,
                            MethodFullName = $"{method.ServerType.FullName.ToLower()}.{method.Name.ToLower()}",
                            MethodDesc = xmlElement.InnerText.Trim(),
                        };

                        Grpc.Paramters.Add(para);
                    }
                }
            }

            if (!rpcList.Any(a => a.ServerName.Equals(Grpc.ServerName)))
            {
                rpcList.Add(Grpc);
            }
        }

        StringBuilder sb = new StringBuilder();

        foreach (var item in rpcList)
        {
            sb.AppendLine();
            sb.AppendLine($"    #region [     {item.ServerName}-{item.ServerDesc}     ]");

            foreach (var method in item.Paramters)
            {
                sb.AppendLine("    /// <summary>");
                sb.AppendLine($"    /// {method.MethodDesc}");
                sb.AppendLine("    /// </summary>");
                sb.AppendLine($"    public const string {method.MethodName} = \"{method.MethodFullName}\";");
                sb.AppendLine();
            }

            sb.AppendLine($"    #endregion");
            //sb.AppendLine();
        }

        string rpc = GetTemplateByFileName("RpcMethod");
        var newStr = rpc.Replace("${context}", sb.ToString()).Replace("${CreateTime}", DateTime.Now.ToString());

        string ShareCommonRpcMethodPath = $"{new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).Parent.Parent.Parent.Parent.FullName}\\Vampirewal.Admin.ShareCommon\\RpcMethod.cs";

        if (File.Exists(ShareCommonRpcMethodPath))
        {
            File.Delete(ShareCommonRpcMethodPath);
        }

        File.AppendAllText(ShareCommonRpcMethodPath, newStr);
    }

    private static string ToTypeNameComment(Type type)
    {
        return (type.Namespace + "." + type.Name).Trim('.');
    }

    /// <summary>
    /// 获取模版文件并转成string
    /// </summary>
    /// <param name="FileName">文件名称</param>
    /// <returns>string</returns>
    private static string GetTemplateByFileName(string FileName)
    {
        Assembly assembly = Assembly.GetExecutingAssembly();

        string[] resNames = assembly.GetManifestResourceNames();
        using (Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.ApiTemplate.{FileName}.txt"))
        {
            if (stream != null)
            {
                using (StreamReader sr = new StreamReader(stream))
                {
                    string context = sr.ReadToEnd();

                    return context;
                }
            }
            else
            {
                return "";
            }
        }
    }

    public static void AddTouchSocket(this IServiceCollection services)
    {
        var service = new TcpTouchRpcService();

        var config = new TouchSocketConfig()//配置
                         .SetListenIPHosts(new IPHost[] { new IPHost(7790), new IPHost(7789) })
                         .UsePlugin()
                         .UseAspNetCoreContainer(services)
                         .SetReceiveType(ReceiveType.Auto)
                         .ConfigureRpcStore(a =>
                         {
                             //a.RegisterServer<MyRpcServer>();//注册服务
                             a.RegisterAllServer();

#if DEBUG
                             //只有不是生产环境才进行生成RpcMethod.cs文件
                             var EnvStr = VampirewalCoreContext.GetInstance().GetContext<string>("EnvironmentOptions");

                             var Env = JsonConvert.DeserializeObject<EnvironmentOptions>(EnvStr);

                             if (Env.IsGenerateRpcMethod)
                             {
                                 a.GenerateApiDocument();//生成一个简单的接口文档
                                 a.GenerateRpcMethodFile();
                             }
#endif
                         })
                         .ConfigurePlugins(a =>
                         {
                             a.Add<JsonRpcParserPlugin>().SetJsonRpcUrl("/jsonrpc");//tcp中路由路径无效

                             if (!Directory.Exists($"{AppDomain.CurrentDomain.BaseDirectory}html"))
                             {
                                 Directory.CreateDirectory($"{AppDomain.CurrentDomain.BaseDirectory}html");
                             }

                             a.Add<HttpStaticPagePlugin>().AddFolder($"{AppDomain.CurrentDomain.BaseDirectory}html");//添加静态页面文件夹

                             //a.Add<TouchRpcActionPlugin<HttpTouchRpcClient>>()
                             // .SetFileTransfering((client, e) =>
                             // {
                             //     string? operation = e.Metadata["Operation"];

                             //     if (!string.IsNullOrEmpty(operation))
                             //     {
                             //         if (operation == "上传附件")
                             //         {
                             //         }
                             //         else if (operation == "发布新版本")
                             //         {
                             //         }
                             //         else if (operation == "下载新版本")
                             //         {
                             //         }
                             //     }
                             // })
                             // .SetFileTransfered((client, e) =>
                             // {
                             //     if (e.Result.ResultCode == ResultCode.Success)
                             //     {
                             //     }
                             // });

                             a.Add<FileOperationPlugin>();

                             a.Add<ClientMutualCallPlugin>();
                         })
                         .SetMaxCount(10000)
                         //.SetThreadCount(100)
                         .SetRootPath($"{AppDomain.CurrentDomain.BaseDirectory}Files")
                         .SetBufferLength(1024 * 1024)
                         .SetVerifyToken("VampirewalAdmin");

        service.Connected += (TcpTouchRpcSocketClient client, TouchSocketEventArgs e) =>
        {
        };

        service.Connecting += (TcpTouchRpcSocketClient client, OperationEventArgs e) =>
        {
        };

        service.IDChanged += (client, e) =>
        {
            try
            {
                //#region ID变化之后，修改用户的在线状态

                //var user = JsonConvert.DeserializeObject<Sys_User>(e.NewID);

                //user.IsOnline = true;

                //#endregion
            }
            catch
            {
            }
        };

        service.Disconnected += (TcpTouchRpcSocketClient client, DisconnectEventArgs e) =>
        {
            try
            {
                //断开连接之后，将清理该用户的数据

                var user = JsonConvert.DeserializeObject<Sys_User>(client.ID);

                user.IsOnline = false;

                VampirewalCoreContext.GetInstance().GetService<IVampirewalCoreEventBusFactory>().Publish("RpcLoginEvent", user);

                //断线之后，移除上下文中的用户数据
                VampirewalCoreContext.GetInstance().RemoveContext(user.BillId);
                VampirewalCoreContext.GetInstance().RemoveContext($"{user.BillId}-httpclient");
            }
            catch
            {
            }
        };

        var TouchRpc = service.Setup(config);
        TouchRpc.Start();

        services.AddSingleton<IService>(TouchRpc);
    }
}

internal class GenerateRPC
{
    public string ServerName { get; set; }

    public string ServerDesc { get; set; }

    public List<RpcMethod> Paramters { get; set; }
}

internal class RpcMethod
{
    public string MethodName { get; set; }

    public string MethodFullName { get; set; }

    public string MethodDesc { get; set; }
}